#include <iostream>
#include <csignal>
#include <unistd.h>
#include <iomanip>

#include "r_socket_proxy/r_socket_proxy.h"

void wait_signals()
{
    sigset_t signs;
    sigemptyset(&signs);
    sigaddset(&signs, SIGINT);
    sigaddset(&signs, SIGKILL);
    sigaddset(&signs, SIGQUIT);
    sigaddset(&signs, SIGTERM);

    pthread_sigmask(SIG_BLOCK, &signs, nullptr);

    int signo(0);
    sigwait(&signs, &signo);
}

std::string vector_to_hex_string(const std::vector<uint8_t>& data) {
    std::ostringstream oss;
    for (size_t i = 0; i < data.size(); ++i) {
        if (i != 0) {
            oss << " ";
        }
        oss << "0x" << std::setw(2) << std::setfill('0') << std::hex 
            << static_cast<int>(data[i]);
    }
    return oss.str();
}

std::string vector_to_compact_hex(const std::vector<uint8_t>& data) {
    std::ostringstream oss;
    oss << std::hex << std::setfill('0');
    for (const auto& byte : data) {
        oss << std::setw(2) << static_cast<int>(byte);
    }
    return oss.str();
}

void received_message_callback(int32_t socket_id, std::vector<uint8_t> &message)
{
    std::cout << "received_message_callback. socket_id = " << socket_id << std::endl;
    std::cout << vector_to_hex_string(message) << std::endl;

    std::vector<std::uint8_t> reponse_data {0x06, 0x07, 0x08, 0x09, 0xA};
    eniac::r_socket_proxy_ret_t ret = eniac::r_socket_proxy::get_instance()->send_message(socket_id, reponse_data);
    std::cout << "received_message_callback. ret = " << (int)ret << std::endl;

}

int main(int argc, char** argv)
{
    eniac::r_socket_proxy::get_instance()->start();

    eniac::r_socket_proxy::get_instance()->set_callback_of_receiving_message(received_message_callback);

    int32_t socket_id_domain_udp_client = eniac::r_socket_proxy::get_instance()->create_socket(eniac::r_socket_type::SOCKET_TYPE_DOMAIN_UDP_CLIENT);
    if (eniac::INVALID_SOCKET_ID == socket_id_domain_udp_client)
    {
        std::cout << "To create socket is failed" << std::endl;
        eniac::r_socket_proxy::get_instance()->stop();
        return 0;
    }

    eniac::r_socket_proxy_ret_t r_ret = eniac::r_socket_proxy::get_instance()->set_socket_property(
                                            socket_id_domain_udp_client,
                                            eniac::r_socket_property_type::SOCKET_PROPERTY_SERVER_ADDRESS,
                                            (void*)"test_udp_addr",
                                            strlen("test_udp_addr"));
    if (eniac::r_socket_proxy_ret_t::OK != r_ret)
    {
        std::cout << "To set server address of socket is failed. ret " << (int)r_ret << std::endl;
        eniac::r_socket_proxy::get_instance()->destory_socket(socket_id_domain_udp_client);
        eniac::r_socket_proxy::get_instance()->stop();
        return 0;
    }

    r_ret = eniac::r_socket_proxy::get_instance()->set_socket_property(
                                            socket_id_domain_udp_client,
                                            eniac::r_socket_property_type::SOCKET_PROPERTY_OWN_ADDRESS,
                                            (void*)"test_udp_addr_client",
                                            strlen("test_udp_addr_client"));
    if (eniac::r_socket_proxy_ret_t::OK != r_ret)
    {
        std::cout << "To set own address of socket is failed. ret " << (int)r_ret << std::endl;
        eniac::r_socket_proxy::get_instance()->destory_socket(socket_id_domain_udp_client);
        eniac::r_socket_proxy::get_instance()->stop();
        return 0;
    }

    r_ret = eniac::r_socket_proxy::get_instance()->start_socket(socket_id_domain_udp_client);
    if (eniac::r_socket_proxy_ret_t::OK != r_ret)
    {
        std::cout << "To start socket is failed. ret " << (int)r_ret << std::endl;
        eniac::r_socket_proxy::get_instance()->destory_socket(socket_id_domain_udp_client);
        eniac::r_socket_proxy::get_instance()->stop();
        return 0;
    }

    std::vector<uint8_t> message {0x01, 0x02, 0x03, 0x04, 0x05};
    r_ret = eniac::r_socket_proxy::get_instance()->send_message(socket_id_domain_udp_client, message);
    if (eniac::r_socket_proxy_ret_t::OK != r_ret)
    {
        std::cout << "To send message is failed. ret " << (int)r_ret << std::endl;
        eniac::r_socket_proxy::get_instance()->stop_socket(socket_id_domain_udp_client);
        eniac::r_socket_proxy::get_instance()->destory_socket(socket_id_domain_udp_client);
        eniac::r_socket_proxy::get_instance()->stop();
        return 0;
    }

    wait_signals();

    eniac::r_socket_proxy::get_instance()->stop_socket(socket_id_domain_udp_client);
    eniac::r_socket_proxy::get_instance()->destory_socket(socket_id_domain_udp_client);
    eniac::r_socket_proxy::get_instance()->stop();
    return 0;
}